popover: make smarter wrt previous focus widget lifetime
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 2 Dec 2014 14:44:04 +0000 (15:44 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Tue, 2 Dec 2014 14:49:23 +0000 (15:49 +0100)
If the previous focus widget is unmapped (eg. hidden, scheduled for
destruction, etc), make the popover forget about it and grant focus
back to the window itself.

gtk/gtkpopover.c

index 83a8111ebc25bbde41532b03f202260c0513981d..ef2badcbecc4b47df3fa6184c776f0b93f195925 100644 (file)
@@ -125,6 +125,7 @@ struct _GtkPopoverPrivate
   GtkAdjustment *vadj;
   GtkAdjustment *hadj;
   GdkRectangle pointing_to;
+  guint prev_focus_unmap_id;
   guint hierarchy_changed_id;
   guint size_allocate_id;
   guint unmap_id;
@@ -234,6 +235,24 @@ gtk_popover_finalize (GObject *object)
   G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object);
 }
 
+static void
+popover_unset_prev_focus (GtkPopover *popover)
+{
+  GtkPopoverPrivate *priv = popover->priv;
+
+  if (!priv->prev_focus_widget)
+    return;
+
+  if (priv->prev_focus_unmap_id)
+    {
+      g_signal_handler_disconnect (priv->prev_focus_widget,
+                                   priv->prev_focus_unmap_id);
+      priv->prev_focus_unmap_id = 0;
+    }
+
+  g_clear_object (&priv->prev_focus_widget);
+}
+
 static void
 gtk_popover_dispose (GObject *object)
 {
@@ -250,11 +269,7 @@ gtk_popover_dispose (GObject *object)
   if (priv->widget)
     gtk_popover_update_relative_to (popover, NULL);
 
-  if (priv->prev_focus_widget)
-    {
-      g_object_unref (priv->prev_focus_widget);
-      priv->prev_focus_widget = NULL;
-    }
+  popover_unset_prev_focus (popover);
 
   G_OBJECT_CLASS (gtk_popover_parent_class)->dispose (object);
 }
@@ -353,6 +368,13 @@ window_set_focus (GtkWindow  *window,
     gtk_widget_hide (GTK_WIDGET (popover));
 }
 
+static void
+prev_focus_unmap_cb (GtkWidget  *widget,
+                     GtkPopover *popover)
+{
+  popover_unset_prev_focus (popover);
+}
+
 static void
 gtk_popover_apply_modality (GtkPopover *popover,
                             gboolean    modal)
@@ -369,8 +391,14 @@ gtk_popover_apply_modality (GtkPopover *popover,
       prev_focus = gtk_window_get_focus (priv->window);
       priv->prev_focus_widget = prev_focus;
       if (priv->prev_focus_widget)
-        g_object_ref (prev_focus);
+        {
+          priv->prev_focus_unmap_id =
+            g_signal_connect (prev_focus, "unmap",
+                              G_CALLBACK (prev_focus_unmap_cb), popover);
+          g_object_ref (prev_focus);
+        }
       gtk_grab_add (GTK_WIDGET (popover));
+      gtk_window_set_focus (priv->window, NULL);
       gtk_widget_grab_focus (GTK_WIDGET (popover));
 
       g_signal_connect (priv->window, "focus-in-event",
@@ -385,15 +413,14 @@ gtk_popover_apply_modality (GtkPopover *popover,
       g_signal_handlers_disconnect_by_data (priv->window, popover);
       gtk_grab_remove (GTK_WIDGET (popover));
 
-      if (priv->prev_focus_widget)
-        {
-          /* Let prev_focus_widget regain focus */
-          if (gtk_widget_is_drawable (priv->prev_focus_widget))
-            gtk_widget_grab_focus (priv->prev_focus_widget);
+      /* Let prev_focus_widget regain focus */
+      if (priv->prev_focus_widget &&
+          gtk_widget_is_drawable (priv->prev_focus_widget))
+        gtk_widget_grab_focus (priv->prev_focus_widget);
+      else
+        gtk_widget_grab_focus (GTK_WIDGET (priv->window));
 
-          g_object_unref (priv->prev_focus_widget);
-          priv->prev_focus_widget = NULL;
-        }
+      popover_unset_prev_focus (popover);
     }
 }
 
@@ -1589,7 +1616,7 @@ gtk_popover_update_relative_to (GtkPopover *popover,
       priv->window = NULL;
     }
 
-  g_clear_object (&priv->prev_focus_widget);
+  popover_unset_prev_focus (popover);
 
   if (priv->widget)
     {